﻿using System;
using System.IO.Pipes;
using System.Text;

namespace SimplePipeHelper.Server
{
    public class SimplePipeServer : IDisposable
    {
        public event Action<bool> PipeServerStart = (ret) => { }; //管道服务端启动
        public event Action PipeServerClosed = () => { }; //管道服务端关闭
        public event Action PipeClientConnect = () => { }; //管道客户端连接
        public event Action PipeClientClosed = () => { }; //管道客户端关闭
        public event Action<byte[]> DataReceived; //管道服务端接收到数据

        public string PipeName { get; set; } //管道名称
        public string LastErrorMessage { get; set; } //错误信息

        private NamedPipeServerStream _pipeServer;
        private readonly PipeServerReadHelper _readHelper;
        private readonly PipeServerWriteHelper _writeHelper;

        private readonly int _maxReadBufferSize;

        public SimplePipeServer(int maxReadBufferSize = 4096)
        {
            _maxReadBufferSize = maxReadBufferSize;
            _readHelper = new PipeServerReadHelper(
                OnPipeClientClosed,
                OnDataReceived,
                OnErrorMessage,
                maxReadBufferSize);
            _writeHelper = new PipeServerWriteHelper(
                OnPipeClientClosed,
                OnErrorMessage);
        }

        #region pipe callback

        private void OnPipeClientClosed()
        {
            PipeClientClosed?.Invoke();
        }

        private void OnDataReceived(byte[] buffer)
        {
            DataReceived?.Invoke(buffer);
        }

        private void OnErrorMessage(string errMsg)
        {
            LastErrorMessage = errMsg;
        }

        #endregion

        public void Start()
        {
            if (string.IsNullOrEmpty(PipeName))
            {
                throw new Exception("PipeName is null or empty.");
            }

            try
            {
                _pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1,
                    PipeTransmissionMode.Byte, PipeOptions.Asynchronous,
                    _maxReadBufferSize, _maxReadBufferSize);

                _pipeServer.BeginWaitForConnection(OnClientConnect, _pipeServer);
                PipeServerStart?.Invoke(true);
            }
            catch (Exception e)
            {
                LastErrorMessage = e.Message;
                PipeServerStart?.Invoke(false);
            }
        }

        public void Stop()
        {
            //停止读写数据
            _readHelper.Stop();
            _writeHelper.Stop();

            if (_pipeServer != null)
            {
                if (_pipeServer.IsConnected)
                {
                    _pipeServer.Disconnect();
                }

                _pipeServer.Dispose();
                _pipeServer = null;

                PipeServerClosed?.Invoke();
            }
        }

        public void SendMessage(string message)
        {
            SendMessage(Encoding.UTF8.GetBytes(message));
        }

        public void SendMessage(byte[] buffer)
        {
            _writeHelper.AddMessage(buffer);
        }

        // ReSharper disable once InconsistentNaming
        private void OnClientConnect(IAsyncResult ar)
        {
            var pipeServer = (NamedPipeServerStream) ar.AsyncState;

            try
            {
                //结束异步操作以等待客户端建立连接
                pipeServer.EndWaitForConnection(ar);
            }
            catch (ObjectDisposedException)
            {
                //管道已关闭
                return;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return;
            }

            //开始读写数据
            _readHelper.Start(pipeServer);
            _writeHelper.Start(pipeServer);

            PipeClientConnect?.Invoke();
        }

        #region IDisposable Support

        //检测冗余调用
        private bool _disposedValue;

        private void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    //释放托管状态(托管对象)。
                    Stop();
                }

                //释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                //将大型字段设置为 null。

                _disposedValue = true;
            }
        }

        ~SimplePipeServer()
        {
            Dispose(false);
        }

        // 添加此代码以正确实现可处置模式。
        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
